package com.photoeditor.demo.ui.widget.image.eraser;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;

import com.common.base.ComponentContext;
import com.common.base.utils.DeviceUtils;
import com.common.base.utils.thread.ThreadPool;
import com.photoeditor.R;
import com.photoeditor.demo.model.image.EdgeAvoidanceHelper;
import com.photoeditor.demo.model.image.util.ImageHelper;

import java.util.Stack;

/**
 * Created by Lai on 2017/12/25 0025.
 */

public class ImageEraserView extends View {

    public float SMALLEST_SIZE = ImageHelper.SCREEN_WIDTH / 2;
    public float MAX_SIZE = ImageHelper.SCREEN_WIDTH * 3f;

    //用于标识是否初始化
    private boolean mIsInit = false;

    //界面的Rect
    private Rect mRect;

    //图片原始rect  都是相对view上的
    private RectF mOriginalRect;
    //图片变换rect
    private RectF mContentRect;

    //GPUImageView原始大小
    private RectF mBoundOriginalRect;
    //GPUImageView变换大小
    private RectF mBoundRect;

    private Bitmap mSrcBitmap;

    //变换的原图 大小为图片可见区域
    private Bitmap mCacheBitmap;
    private Canvas mCanvas;

    private Bitmap mShaderBitmap;
    private BitmapShader mBitmapShader;

    //用于混合
    private Bitmap mRenderBitmap;
    private Canvas mRenderCanvas;

    private Paint mBitmapPaint;
    private Path mPath;
    private float mPathDownX, mPathDownY;

    private Paint mRingPaint;
    private Paint mCirclePaint;

    //各操作列表
    private Stack<Operation> mPathOperateList;
    //擦除和还原的画笔
    private Paint mPaint;

    private final static float MIN_SIZE = DeviceUtils.dip2px(18);
    private final static float VARIABLE_SIZE = DeviceUtils.dip2px(122);
    private final static float DEFAULT_SIZE = DeviceUtils.dip2px(54.6f);

    public float mPaintWidthErase = DEFAULT_SIZE;
    public float mPaintWidthReverse = DEFAULT_SIZE;

    private PorterDuffXfermode mEraseXfermode;
    private PorterDuffXfermode mReverseXfermode;
    private BlurMaskFilter mBlurMaskFilter;

    public static final int PAINT_STYLE_ERASE = 1;
    public static final int PAINT_STYLE_REVERSE = 2;

    private boolean mBuildCacheBitmap = false;
    private boolean mShowSrc = false;
    private PorterDuffXfermode mExchangeXfermode;
    private boolean mForMixer = false;
    private boolean mDrawPaintWidthCircle = false;

    private int mPaintStyle = PAINT_STYLE_ERASE;

    private float mDegree = 0;
    private EdgeAvoidanceHelper mEdgeAvoidanceHelper;

    private EraserBgView mEraserBgView;
    private PointF mPointF;

    //移动时的临界
    private RectF mMoveBorderRect;

    public ImageEraserView(@NonNull Context context) {
        this(context, null);
    }

    public ImageEraserView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ImageEraserView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    private void initialize() {

        setLayerType(LAYER_TYPE_SOFTWARE, null);

        mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);

        mPath = new Path();

        mEraseXfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);
        mReverseXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_OVER);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mPaintWidthErase);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeJoin(Paint.Join.ROUND);

        setPaintStyle(PAINT_STYLE_ERASE);

        mRingPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mRingPaint.setStyle(Paint.Style.STROKE);
        mRingPaint.setColor(Color.WHITE);
        mRingPaint.setStrokeWidth(DeviceUtils.dip2px(ComponentContext.getContext(), 2));

        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCirclePaint.setStyle(Paint.Style.FILL);
        mCirclePaint.setColor(Color.parseColor("#33000000"));
        mEdgeAvoidanceHelper = new EdgeAvoidanceHelper();
        mEdgeAvoidanceHelper.setStrokeWidth((int) mPaintWidthErase);

        mBoundOriginalRect = new RectF();
        mBoundRect = new RectF();
        mContentRect = new RectF();
        mMoveBorderRect = new RectF();

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (!mIsInit) {
            mRect = new Rect(0, 0, right - left, bottom - top);
            if (!mForMixer) {
                mBoundOriginalRect.set(mRect);
                if (mOriginalRect == null) {
                    mOriginalRect = new RectF(mRect);
                } else {
                    float width = mOriginalRect.width();
                    float height = mOriginalRect.height();
                    mOriginalRect.left = mRect.centerX() - width / 2;
                    mOriginalRect.top = mRect.centerY() - height / 2;
                    mOriginalRect.right = mOriginalRect.left + width;
                    mOriginalRect.bottom = mOriginalRect.top + height;
                }
            }
            if (mBuildCacheBitmap && mSrcBitmap != null) {
                createCacheBitmap();
                mBuildCacheBitmap = false;
            }
            mIsInit = true;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!mIsInit || mCanvas == null) {
            return;
        }

        if (mShowSrc) {
            if (mShaderBitmap != null && !mShaderBitmap.isRecycled()) {
                if (mForMixer && mEraserRefreshBitmapListener != null) {
                    mEraserRefreshBitmapListener.refreshBitmap(mShaderBitmap, mContentRect, mBoundRect);
                } else {
                    canvas.drawBitmap(mShaderBitmap, null, mContentRect, mBitmapPaint);
                }
            }
        } else {
            if (mRenderBitmap != null && !mRenderBitmap.isRecycled()) {
                if (mForMixer && mEraserRefreshBitmapListener != null) {
                    mEraserRefreshBitmapListener.refreshBitmap(mRenderBitmap, mContentRect, mBoundRect);
                } else {
                    canvas.drawBitmap(mRenderBitmap, null, mContentRect, mBitmapPaint);
                }
            }
        }

        drawPaintWidthCircle(canvas);
    }

    private void drawPaintWidthCircle(Canvas canvas) {
        if (mDrawPaintWidthCircle) {
            float width = mPaintWidthErase / 2;
            if (mPaintStyle == PAINT_STYLE_REVERSE) {
                width = mPaintWidthReverse / 2;
            }
            canvas.drawCircle(mRect.centerX(), mRect.centerY(), width, mCirclePaint);
            canvas.drawCircle(mRect.centerX(), mRect.centerY(), width, mRingPaint);
        }
    }

    boolean mTwoFingers = false;
    boolean mMoving = false;
    float mTouchDownX, mTouchDownY;
    float mCenterX, mCenterY;
    float oldDist = 1f;
    boolean mScaleAndMove = false;
    private static final float TouchSlop = ViewConfiguration.get(ComponentContext.getContext()).getScaledTouchSlop();

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!mIsInit || mCanvas == null || mSrcBitmap == null || mSrcBitmap.isRecycled()) {
            return false;
        }
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                if (mEraserListener != null) {
                    mEraserListener.onTouchDown();
                }
                mMoving = false;
                if (event.getPointerCount() != 1) {
                    mTwoFingers = true;
                    break;
                }
                mTwoFingers = false;
                mTouchDownX = event.getX();
                mTouchDownY = event.getY();
                mPointF = new PointF(mTouchDownX, mTouchDownY);
                if (mPaintStyle == PAINT_STYLE_ERASE) {
                    mPointF = mEdgeAvoidanceHelper.onTouchEvent(event);
                }
                mPathDownX = mPointF.x;
                mPathDownY = mPointF.y;

                moveTo(mPathDownX - mContentRect.left, mPathDownY - mContentRect.top);
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                ThreadPool.removeUITaskCallbacks(mResetEdgeAvoidanceRunnable);
                mTwoFingers = true;
                if (mMoving) {
                    savePath();
                }
                mCenterX = (event.getX(0) + event.getX(1)) / 2.0f;
                mCenterY = (event.getY(0) + event.getY(1)) / 2.0f;
                oldDist = spacing(event);
                mScaleAndMove = false;
                mMoving = false;
                if (mForMixer) {
                    if (mBoundOriginalRect.contains(event.getX(0), event.getY(0))) {
                        mScaleAndMove = true;
                    }
                } else {
                    if (mOriginalRect.contains(event.getX(0), event.getY(0))) {
                        mScaleAndMove = true;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (!mTwoFingers && event.getPointerCount() == 1) {
                    float touchDownX = event.getX();
                    float touchDownY = event.getY();
                    if (!mMoving && Math.abs(touchDownX - mTouchDownX) < TouchSlop && Math.abs(touchDownY - mTouchDownY) < TouchSlop) {
                        break;
                    }
                    mMoving = true;
                    mTwoFingers = false;
                    mPointF = new PointF(touchDownX, touchDownY);
                    if (mPaintStyle == PAINT_STYLE_ERASE) {
                        mPointF = mEdgeAvoidanceHelper.onTouchEvent(event);
                    }
                    float pathNowX = mPointF.x;
                    float pathNowY = mPointF.y;
                    quadTo(mPathDownX - mContentRect.left, mPathDownY - mContentRect.top, pathNowX - mContentRect.left, pathNowY - mContentRect.top);
                    mPathDownX = pathNowX;
                    mPathDownY = pathNowY;
                    mTouchDownX = touchDownX;
                    mTouchDownY = touchDownY;
                } else if (event.getPointerCount() > 1 && mScaleAndMove) {
                    float newDist = spacing(event);
                    float centerX = (event.getX(0) + event.getX(1)) / 2.0f;
                    float centerY = (event.getY(0) + event.getY(1)) / 2.0f;

                    float scale = newDist / oldDist;
                    setScale(scale, centerX, centerY);
                    oldDist = newDist;

                    if (mMoving || Math.abs(centerX - mCenterX) > TouchSlop
                            || Math.abs(centerY - mCenterY) > TouchSlop) {
                        mMoving = true;
                        setMove(centerX - mCenterX, centerY - mCenterY);
                        mCenterX = centerX;
                        mCenterY = centerY;
                    }
                    mTwoFingers = true;
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                mTwoFingers = true;
                if (mPaintStyle == PAINT_STYLE_REVERSE) {
                    updatePaintWidth(mPaintWidthReverse);
                } else {
                    updatePaintWidth(mPaintWidthErase);
                }
                ThreadPool.runUITask(mResetEdgeAvoidanceRunnable, 80);
                break;
            case MotionEvent.ACTION_UP:
                if (!mTwoFingers && event.getPointerCount() == 1) {
                    savePath();
                }
                break;

        }
        invalidate();
        return true;

    }

    private void setMove(float dx, float dy) {
        if (mForMixer) {
            RectF rectF = new RectF(mBoundRect);
            rectF.offset(dx, dy);
            float trueDx = dx;
            float trueDy = dy;
            if (rectF.left > mMoveBorderRect.right) {
                trueDx = mMoveBorderRect.right - mBoundRect.left;
            }
            if (rectF.right < mMoveBorderRect.left) {
                trueDx = mMoveBorderRect.left - mBoundRect.right;
            }
            if (rectF.top > mMoveBorderRect.bottom) {
                trueDy = mMoveBorderRect.bottom - mBoundRect.top;
            }
            if (rectF.bottom < mMoveBorderRect.top) {
                trueDy = mMoveBorderRect.top - mBoundRect.bottom;
            }
            mBoundRect.offset(trueDx, trueDy);
            mContentRect.offset(trueDx, trueDy);
        } else {
            RectF rectF = new RectF(mContentRect);
            rectF.offset(dx, dy);
            float trueDx = dx;
            float trueDy = dy;
            if (rectF.left > mMoveBorderRect.right) {
                trueDx = mMoveBorderRect.right - mContentRect.left;
            }
            if (rectF.right < mMoveBorderRect.left) {
                trueDx = mMoveBorderRect.left - mContentRect.right;
            }
            if (rectF.top > mMoveBorderRect.bottom) {
                trueDy = mMoveBorderRect.bottom - mContentRect.top;
            }
            if (rectF.bottom < mMoveBorderRect.top) {
                trueDy = mMoveBorderRect.top - mContentRect.bottom;
            }
            mContentRect.offset(trueDx, trueDy);
            if (mEraserBgView != null) {
                mEraserBgView.updateContentRect(mContentRect);
            }
        }
    }

    private void setScale(float scale, float centerX, float centerY) {
        Matrix m = new Matrix();
        RectF rect = new RectF();
        if (mForMixer) {
            RectF rectGPU = new RectF();
            m.setScale(scale, scale, centerX, centerY);
            m.mapRect(rectGPU, mBoundRect);
            if (rectGPU.width() >= SMALLEST_SIZE && rectGPU.width() <= MAX_SIZE) {
                mBoundRect = rectGPU;
                m.setScale(scale, scale, centerX, centerY);
                m.mapRect(rect, mContentRect);
                mContentRect = rect;
            }
        } else {
            m.setScale(scale, scale, centerX, centerY);
            m.mapRect(rect, mContentRect);
            if (rect.width() >= SMALLEST_SIZE && rect.width() <= MAX_SIZE) {
                mContentRect = rect;
                if (mEraserBgView != null) {
                    mEraserBgView.updateContentRect(mContentRect);
                }
            }
        }

    }

    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return (float) Math.sqrt(x * x + y * y);
    }

    private void moveTo(float x, float y) {
        float[] dest = new float[2];
        getRotateMatrix().mapPoints(dest, new float[]{x, y});
        getScaleMatrix().mapPoints(dest, new float[]{dest[0], dest[1]});
        mPath.moveTo(dest[0], dest[1]);
    }

    private void quadTo(float x1, float y1, float x2, float y2) {
        float[] point1 = new float[2];
        float[] point2 = new float[2];
        getRotateMatrix().mapPoints(point1, new float[]{x1, y1});
        getScaleMatrix().mapPoints(point1, new float[]{point1[0], point1[1]});
        getRotateMatrix().mapPoints(point2, new float[]{x2, y2});
        getScaleMatrix().mapPoints(point2, new float[]{point2[0], point2[1]});

        mPath.quadTo(point1[0], point1[1], (point1[0] + point2[0]) / 2, (point1[1] + point2[1]) / 2);

        mRenderCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
        mRenderCanvas.drawBitmap(mCacheBitmap, 0, 0, mBitmapPaint);
        mRenderCanvas.drawPath(mPath, mPaint);
    }

    //保存路径
    private void savePath() {
        if (!mPath.isEmpty()) {
            if (mPathOperateList == null) {
                mPathOperateList = new Stack<>();
            }
            PathOperation operation = new PathOperation(mPath, mPaint);
            operation.operate(mCanvas);
            mRenderCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
            mRenderCanvas.drawBitmap(mCacheBitmap, 0, 0, mBitmapPaint);
            mPathOperateList.push(operation);
            mPath.reset();
            if (mEraserListener != null) {
                mEraserListener.onPaintFinish(mPathOperateList.size());
            }
        }
    }

    private Matrix getRotateMatrix() {
        Matrix matrix = new Matrix();
        if (mDegree != 0) {
            matrix.setRotate(-mDegree, mContentRect.width() / 2.0f, mContentRect.height() / 2.0f);
        }
        return matrix;
    }

    private Matrix getScaleMatrix() {
        Matrix matrix = new Matrix();
        float scale = mOriginalRect.width() / mContentRect.width();
        matrix.setScale(scale, scale);
        return matrix;
    }

    public void exchange() {
        if (mExchangeXfermode == null) {
            mExchangeXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT);
        }
        mBitmapPaint.setXfermode(mExchangeXfermode);
        BitmapOperation operation = new BitmapOperation(mShaderBitmap, mBitmapPaint);
        operation.operate(mCanvas);
        mBitmapPaint.setXfermode(null);
        mRenderCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
        mRenderCanvas.drawBitmap(mCacheBitmap, 0, 0, mBitmapPaint);
        invalidate();
        if (mPathOperateList == null) {
            mPathOperateList = new Stack<>();
        }
        mPathOperateList.push(operation);
        if (mEraserListener != null) {
            mEraserListener.onPaintFinish(mPathOperateList.size());
        }
    }

    public void undo() {
        if (mPathOperateList != null && mPathOperateList.size() - 1 >= 0) {
            mPathOperateList.pop();

            mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
            mCanvas.drawBitmap(mSrcBitmap, null, new RectF(0, 0, mOriginalRect.width(), mOriginalRect.height()), mBitmapPaint);

            for (Operation operation : mPathOperateList) {
                operation.operate(mCanvas);
            }
            mRenderCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
            mRenderCanvas.drawBitmap(mCacheBitmap, 0, 0, mBitmapPaint);
            invalidate();
            if (mEraserListener != null) {
                mEraserListener.onPaintFinish(mPathOperateList.size());
            }
        }
    }

    public void resetBitmap() {
        if (mPathOperateList != null) {
            mPathOperateList.clear();
        }
        mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
        mCanvas.drawBitmap(mSrcBitmap, null, new RectF(0, 0, mOriginalRect.width(), mOriginalRect.height()), mBitmapPaint);
        mRenderCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC);
        mRenderCanvas.drawBitmap(mCacheBitmap, 0, 0, mBitmapPaint);
        invalidate();
        if (mEraserListener != null) {
            mEraserListener.onPaintFinish(0);
        }
    }

    public void setShowSrc(boolean showSrc) {
        mShowSrc = showSrc;
        invalidate();
    }

    private void createCacheBitmap() {
        if (mSrcBitmap == null) {
            return;
        }
        if (mCacheBitmap != null && !mCacheBitmap.isRecycled()) {
            mCacheBitmap.recycle();
        }
        if (mForMixer) {
            SMALLEST_SIZE = mBoundOriginalRect.width();
            MAX_SIZE = mBoundOriginalRect.width() * 50;
            float quarterWidth = mRect.width() / 4;
            mMoveBorderRect.set(mBoundOriginalRect.left + quarterWidth, mBoundOriginalRect.top + quarterWidth, mBoundOriginalRect.right - quarterWidth, mBoundOriginalRect.bottom - quarterWidth);
            mBoundRect.set(mBoundOriginalRect);
            ThreadPool.runUITask(mResetEdgeAvoidanceRunnable, 1);
        } else {
            mContentRect.set(mOriginalRect);
            SMALLEST_SIZE = mOriginalRect.width();
            MAX_SIZE = mOriginalRect.width() * 50;
            float quarterWidth = mRect.width() / 4;
            mMoveBorderRect.set(mRect.left + quarterWidth, mRect.top + quarterWidth, mRect.right - quarterWidth, mRect.bottom - quarterWidth);
            if (mEraserBgView != null) {
                mEraserBgView.createBgBitmap(mContentRect);
                mEraserBgView.setVisibility(VISIBLE);
            }
        }
        mCacheBitmap = Bitmap.createBitmap((int) mOriginalRect.width(), (int) mOriginalRect.height(), Bitmap.Config.ARGB_8888);
        mCanvas = new Canvas(mCacheBitmap);
        mCanvas.drawBitmap(mSrcBitmap, null, new RectF(0, 0, mOriginalRect.width(), mOriginalRect.height()), mBitmapPaint);

        mShaderBitmap = mCacheBitmap.copy(Bitmap.Config.ARGB_8888, false);

        mRenderBitmap = mCacheBitmap.copy(mCacheBitmap.getConfig(), true);
        mRenderCanvas = new Canvas(mRenderBitmap);
    }

    public void setPaintStyle(int style) {
        mPaintStyle = style;
        if (style == PAINT_STYLE_ERASE) {
            try {
                mPaint.setShader(null);
                mPaint.setXfermode(mEraseXfermode);
                updatePaintWidth(mPaintWidthErase);
                mPaint.setColor(0x00ffffff);
                mEdgeAvoidanceHelper.setStrokeWidth((int) mPaintWidthErase);
            } catch (Exception e) {
            }
        } else if (style == PAINT_STYLE_REVERSE) {
            try {
                if (mBitmapShader == null) {
                    mBitmapShader = new BitmapShader(
                            mShaderBitmap,
                            Shader.TileMode.CLAMP,
                            Shader.TileMode.CLAMP);
                }
                mPaint.setShader(mBitmapShader);
                updatePaintWidth(mPaintWidthReverse);
                mPaint.setXfermode(mReverseXfermode);
                mPaint.setColor(0xffffffff);
                mEdgeAvoidanceHelper.setStrokeWidth((int) mPaintWidthReverse);
            } catch (Exception e) {
            }
        }
    }

    public void setSrcBitmap(Bitmap srcBitmap) {
        mForMixer = false;
        mSrcBitmap = srcBitmap;
        mBuildCacheBitmap = !mIsInit;
        mDegree = 0;
        if (mIsInit) {
            mBoundOriginalRect.set(mRect);
            createCacheBitmap();
        }
        invalidate();
        onDrawContentChanged();
    }

    public void setEraserBgView(EraserBgView eraserBgView) {
        mEraserBgView = eraserBgView;
    }

    private ViewTreeObserver.OnDrawListener mOnDrawListener = new ViewTreeObserver.OnDrawListener() {
        @Override
        public void onDraw() {
            if (!mForMixer) {
                ThreadPool.runUITask(mResetEdgeAvoidanceRunnable, 1000);
            }
            ThreadPool.runUITask(new Runnable() {
                @Override
                public void run() {
                    getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
                }
            });
        }
    };

    /**
     * 绘制内容发生变化后，需要重新进行边缘检测
     */
    private void onDrawContentChanged() {
        getViewTreeObserver().addOnDrawListener(mOnDrawListener);
    }

    private Runnable mResetEdgeAvoidanceRunnable = new Runnable() {
        @Override
        public void run() {
            Bitmap content = null;
            if (!mForMixer) {
                destroyDrawingCache();
                buildDrawingCache();
                content = getDrawingCache();
            } else {
                // mRenderBitmap 大小比View小，边缘检测的bitmap需要使用view大小
                Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), mRenderBitmap.getConfig());
                Canvas canvas = new Canvas(bitmap);
                canvas.rotate(mDegree, mContentRect.centerX(), mContentRect.centerY());
                canvas.drawBitmap(mRenderBitmap, null, mContentRect, mBitmapPaint);
                content = bitmap;
            }
            mEdgeAvoidanceHelper.setBitampAsyncCanny(content);
        }
    };

    public void setSrcBitmap(Bitmap srcBitmap, RectF originalRect, RectF contentRect, RectF boundRect, float degree) {
        mForMixer = true;
        mSrcBitmap = srcBitmap;
        mBuildCacheBitmap = !mIsInit;
        mOriginalRect = new RectF(originalRect);
        mContentRect.set(contentRect);
        mBoundOriginalRect.set(boundRect);
        mDegree = degree;
        if (mIsInit) {
            createCacheBitmap();
        }
        if (mPaintStyle == PAINT_STYLE_REVERSE) {
            updatePaintWidth(mPaintWidthReverse);
        } else {
            updatePaintWidth(mPaintWidthErase);
        }
        invalidate();
        onDrawContentChanged();
    }

    public void updateContentRect(int width, int height) {
        if (mIsInit) {
            mOriginalRect.left = mRect.centerX() - width / 2;
            mOriginalRect.top = mRect.centerY() - height / 2;
            mOriginalRect.right = mOriginalRect.left + width;
            mOriginalRect.bottom = mOriginalRect.top + height;
            invalidate();
        } else {
            mOriginalRect = new RectF(0, 0, width, height);
        }
    }

    public Bitmap getDestBitmap() {
        if (!mIsInit || mSrcBitmap == null || mSrcBitmap.isRecycled()) {
            return mSrcBitmap;
        }
        int width = mSrcBitmap.getWidth();
        int height = mSrcBitmap.getHeight();
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Rect rect = new Rect(0, 0, width, height);
        Bitmap bgBitmap = null;
        if (mEraserBgView != null) {
            bgBitmap = mEraserBgView.getTransparentBgBitmap();
        }
        if (bgBitmap == null) {
            bgBitmap = createBgBitmap();
        }
        canvas.drawBitmap(bgBitmap, null, rect, mBitmapPaint);
        //cacheBitmap清晰度下降，再使用src盖在上面
        canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
        canvas.drawBitmap(mCacheBitmap, null, rect, mBitmapPaint);
        mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mSrcBitmap, 0, 0, mBitmapPaint);
        mBitmapPaint.setXfermode(null);
        canvas.restore();
        return bitmap;
    }

    public Bitmap createBgBitmap() {
        int bgWidth = (int) mContentRect.width();
        int bgHeight = (int) mContentRect.height();
        Bitmap bgBitmap = Bitmap.createBitmap(bgWidth, bgHeight, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bgBitmap);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_transparent_grid);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);
        paint.setShader(new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
        canvas.drawRect(0, 0, bgWidth, bgHeight, paint);
        return bgBitmap;
    }

    public Bitmap getDestBitmapForMixer() {
        if (!mIsInit || mSrcBitmap == null || mSrcBitmap.isRecycled()) {
            return mSrcBitmap;
        }
        int width = mSrcBitmap.getWidth();
        int height = mSrcBitmap.getHeight();
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Rect rect = new Rect(0, 0, width, height);
        canvas.drawBitmap(mCacheBitmap, null, rect, mBitmapPaint);
        mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mSrcBitmap, 0, 0, mBitmapPaint);
        mBitmapPaint.setXfermode(null);
        return bitmap;
    }

    public void reset() {
        ThreadPool.removeUITaskCallbacks(mResetEdgeAvoidanceRunnable);
        if (mPathOperateList != null) {
            mPathOperateList.clear();
        }
        mPath.reset();

        mBitmapShader = null;

        mCacheBitmap = null;
        mSrcBitmap = null;
        mRenderBitmap = null;
        mShaderBitmap = null;

        if (mEraserBgView != null) {
            mEraserBgView.reset();
        }
        mPaintWidthErase = DEFAULT_SIZE;
        mPaintWidthReverse = DEFAULT_SIZE;
        mContentRect.setEmpty();
        setPaintStyle(PAINT_STYLE_ERASE);
        mForMixer = false;
        mDegree = 0;
        invalidate();
        mEdgeAvoidanceHelper.onDestroy();
    }

    private class PathOperation extends Operation {
        public Path path;
        public Paint paint;

        public PathOperation(Path path, Paint paint) {
            this.path = new Path(path);
            this.paint = new Paint(paint);
        }

        public void operate(Canvas canvas) {
            canvas.save();
            canvas.drawPath(path, paint);
            canvas.restore();
        }
    }

    private class BitmapOperation extends Operation {
        public Bitmap bitmap;
        public Paint paint;

        public BitmapOperation(Bitmap bitmap, Paint paint) {
            this.bitmap = bitmap;
            this.paint = new Paint(paint);
        }

        @Override
        void operate(Canvas canvas) {
            canvas.drawBitmap(bitmap, 0, 0, paint);
        }
    }

    public void setDrawPaintWidthCircle(boolean drawPaintWidthCircle) {
        mDrawPaintWidthCircle = drawPaintWidthCircle;
        invalidate();
    }

    public void setPaintWidthErase(int progress) {
        try {
            mPaintWidthErase = MIN_SIZE + VARIABLE_SIZE * progress / 100;
            updatePaintWidth(mPaintWidthErase);
            mEdgeAvoidanceHelper.setStrokeWidth((int) mPaintWidthErase);
            invalidate();
        } catch (Exception e) {

        }
    }

    public void setPaintWidthRepair(int progress) {
        try {
            mPaintWidthReverse = MIN_SIZE + VARIABLE_SIZE * progress / 100;
            updatePaintWidth(mPaintWidthReverse);
            mEdgeAvoidanceHelper.setStrokeWidth((int) mPaintWidthReverse);
            invalidate();
        } catch (Exception e) {

        }
    }

    private void updatePaintWidth(float width) {
        float v = width;
        if (mOriginalRect != null && !mOriginalRect.isEmpty() && mContentRect != null && !mContentRect.isEmpty()) {
            v = width * mOriginalRect.width() / mContentRect.width();
        }
        mBlurMaskFilter = new BlurMaskFilter(v / 8, BlurMaskFilter.Blur.NORMAL);
        mPaint.setMaskFilter(mBlurMaskFilter);
        mPaint.setStrokeWidth(v);
    }

    private abstract class Operation {
        abstract void operate(Canvas canvas);
    }

    private EraserListener mEraserListener;

    public void setEraserListener(EraserListener eraserListener) {
        mEraserListener = eraserListener;
    }

    public interface EraserListener {
        void onPaintFinish(int size);

        void onTouchDown();
    }

    private EraserRefreshBitmapListener mEraserRefreshBitmapListener;

    public void setEraserRefreshBitmapListener(EraserRefreshBitmapListener eraserRefreshBitmapListener) {
        mEraserRefreshBitmapListener = eraserRefreshBitmapListener;
    }

    public interface EraserRefreshBitmapListener {
        void refreshBitmap(Bitmap bitmap, RectF upperRectF, RectF bottomRect);

    }
}
